import math
import os.path

import cv2
import numpy as np
from moviepy.editor import *

import lrc_utils
from base import Base, get_mp3_duration


class Demo(Base):

    def __init__(self, root_input_path, root_output_path):
        super().__init__(root_input_path, root_output_path, 704, 1280)
        self.cover_output = super().real_path("cover", self.temp)  # 封面视频输出路径
        self.bg_video_out_dir = super().real_path("bg_pic", self.temp)  # 背景图片转视频输出路径
        self.bg_video_mp3_out_dir = super().real_path("merge_mp3", self.temp)  # 视频合并音乐
        self.final_video_output = super().real_path("final", self.output)  # 最终视频输出路径
        self.font_path = super().real_path("font", self.input)  # 字体路径
        mp3_lrc_files = super().classify_files("mp3", [".mp3", ".lrc"])  # mp3 存放文件
        self.bg_pics = super().list_files("bg_pic", ".png")  # mp3 存放文件
        self.mp3_files = mp3_lrc_files[".mp3"]  # mp3 存放文件 这里是绝对路径
        self.lrc_files = mp3_lrc_files[".lrc"]  # mp3 存放的歌词文件
        # 由于 TextClip 不支持 \\ 路径将\\ 改为/
        self.font_style = super().real_path("AlibabaPuHuiTi-3-95-ExtraBold.ttf", self.font_path).replace(os.sep, '/')
        self.title_font_style = super().real_path("SanJiXingKaiJianTi-Cu-2.ttf", self.font_path).replace(os.sep, '/')
        # 横向移动
        self.step = 2
        self.pic_show_duration = 0.08  # 单位是s
        # 批量合并 音乐
        for mp3_name in self.mp3_files:
            mp3_duration = get_mp3_duration(mp3_name)
            lrc_name = [lrc_file for lrc_file in self.lrc_files if lrc_file.startswith(mp3_name[:-4])].pop()
            lrc_info = self.init_lyrics(lrc_name)
            # 批量生成背景 并且合并封面
            for pic_name in self.bg_pics:
                # 生成背景视频
                self.draw_bg(pic_name, mp3_duration)
                # 开始合并操作
                self.merge(pic_name, mp3_name, lrc_info)
            pass

    def init_lyrics(self, name):

        return lrc_utils.lrc_parse(name)

    def draw_bg(self, name, duration):
        print(f"mp3 的 duration {duration}")
        bg_file = super().real_path(f"bg_pic/{name}", self.input)  # 背景图片转视频输出路径
        bg_video_file = super().real_path(f"bg_video_{name}.mp4", self.bg_video_out_dir)
        if not os.path.exists(bg_video_file):  # 如果没有合成进行合成
            print(f"背景视频开始合成{bg_video_file}")
            # 读取图片
            image = cv2.imread(bg_file)
            img_height, img_width, channels = image.shape
            if img_height < self.width or img_width < img_height:
                assert f"图片尺寸不合适: height:{img_height}, width:{img_width}"
            width_size = img_width
            height_size = img_height
            if img_height > self.height:
                scale = img_height / self.height
                width_size = int(img_width / scale)
                height_size = img_height
                image = cv2.resize(image, (width_size, height_size))
                print(f'scale:{scale}, width_size:{width_size}, height_size:{height_size}')
            max_mov = img_width - self.width
            max_image_range = range(0, max_mov, self.step)
            # 左移生成图片
            print(f"左移生成图片共{len(max_image_range)}")
            left_shifted_images = [
                self.move(image, i, 0, self.pic_show_duration, (width_size, height_size), 0)
                for i in max_image_range]

            # 右移生成图片
            print(f"右移生成图片共{len(max_image_range)}")
            right_shifted_images = [
                self.move(image, -i, 0, self.pic_show_duration, (width_size, height_size), 1)
                for i in max_image_range]

            # 合并所有图片生成视频
            print(f"合并所有图片生成视频")
            left_shifted_images.extend(right_shifted_images)

            bg_video_len = len(left_shifted_images) * self.pic_show_duration
            print(f"mp3 的 duration {duration} 每组动画需要{bg_video_len}")
            # 如果视频小于音乐的长度计算 重复的次数 继续合并
            last_images = []
            if bg_video_len < duration:
                repeat_count = math.ceil((duration - bg_video_len) // bg_video_len)
                last_duration = duration - bg_video_len * (repeat_count + 1)
                print(f"视频重复循环次数{repeat_count}剩余时间{last_duration}")
                last_images.extend(left_shifted_images)
                # 先计算整的
                for x in range(repeat_count):
                    last_images.extend(left_shifted_images)
                # 在计算 散的
                if last_duration < bg_video_len:
                    last_count = math.ceil(last_duration // self.pic_show_duration)
                    print(f"视频重还需次数{last_count}")
                    last_clip = last_images[:last_count]
                    last_images.extend(last_clip)

            merged_video = concatenate_videoclips(last_images)
            # 保存视频
            merged_video.write_videofile(bg_video_file, codec="libx264", fps=24)
        else:
            print(f"背景视频已合成:{bg_video_file}")
        pass

    def move(self, img, dx, dy, duration, size, direction):
        # print(f"duration={duration}")
        height, width, channels = img.shape
        width_size = size[0]
        # 定义仿射变换矩阵
        m = np.float32([[1, 0, dx], [0, 1, dy]])
        # 应用仿射变换
        shifted = cv2.warpAffine(img, m, (width, height))
        if direction == 0:
            shifted = shifted[:, width_size - self.width: width_size]
        else:
            shifted = shifted[:, :self.width]
        return ImageClip(shifted, duration=duration)

    # "ti"
    # 代表歌曲名。
    # "ar"
    # 代表艺术家名。
    # "al"
    # 代表专辑名。
    # "by"
    # 代表编辑者（LRC歌词的制作人）。
    # "offset"
    # 代表时间补偿值，用于调整显示的时间快慢。
    def generate_cover(self, name, lrc_info):
        print(f"开始生成封面视频{name}")
        cover_output = super().real_path(f"cover_video_{name}.mp4", self.cover_output)
        if not os.path.exists(cover_output):  # 如果没有合成进行合成
            print(f"背景视频开始合成{cover_output}")
            cover_video = self.get_bg_video_cover(name, lrc_info)
            title_duration = cover_video.duration
            title = f"{lrc_info['ti']}-{lrc_info['ar']}"
            print(f"title{title}title_duration:{title_duration}")
            w, h = cover_video.w, cover_video.h
            title_txt = TextClip(txt=title, fontsize=30, font=self.title_font_style, size=(31 * len(title), None),
                                 align='center', color='white', method="caption", stroke_color="black",
                                 stroke_width=0).set_position(("center", (h * 3) // 16)).set_duration(
                title_duration).set_start(0).crossfadein(1).crossfadeout(1)
            # 合成字幕
            cover_video = CompositeVideoClip([cover_video, title_txt])
            print(f"已经生成封面视频{name}")
            cover_video.write_videofile(cover_output)
        else:
            print(f"封面视频已合成")
            cover_video = VideoFileClip(cover_output)
            return cover_video

    def get_bg_video_cover(self, pic_name, lrc_info):
        bg_video_file = super().real_path(f"bg_video_{pic_name}.mp4", self.bg_video_out_dir)
        cover_duration = self.pic_show_duration * 100
        if 'lrc_list' in lrc_info:
            cover_duration = lrc_info['lrc_list'][0]["end_time"]
        # 读取代码视频
        my_clip = VideoFileClip(bg_video_file).subclip(0, cover_duration)
        return my_clip

    def merge(self, pic_name, mp3, lrc):
        mp3filename = os.path.basename(mp3)
        final_video_path = super().real_path(f"video_music_{mp3filename}.mp4", self.final_video_output)
        if os.path.exists(final_video_path):  # 如果没有合成进行合成
            print(f"视频已合成:{final_video_path}")
            return
            # 生成封面
        cover_clip = self.generate_cover(pic_name, lrc)
        # bg 和 mp3 合并
        bj_mp3_clip = self.merge_music(pic_name, mp3)
        bj_cover_mp3_text_clip = self.merge_text(mp3filename, lrc)
        final_video = CompositeVideoClip([bj_mp3_clip, cover_clip, *bj_cover_mp3_text_clip, ])
        final_video.write_videofile(final_video_path, audio_codec='aac', preset='superfast')
        pass

    def merge_music(self, pic_name, mp3):
        filename = os.path.basename(mp3)
        print(f"开始合并背景{pic_name}音乐{filename}")
        bg_video_file = super().real_path(f"bg_video_{pic_name}.mp4", self.bg_video_out_dir)
        video_mp3_output = super().real_path(f"video_mp3{pic_name}.mp4", self.bg_video_mp3_out_dir)
        if not os.path.exists(video_mp3_output):  # 如果没有合成进行合成
            # 读取代码视频
            my_clip = VideoFileClip(bg_video_file)
            # 截取背景音乐
            audio_background = AudioFileClip(mp3)
            audio_background.set_duration(my_clip.duration)
            # 视频中插入音频
            final_clip = my_clip.set_audio(audio_background)
            print(f"已经合并背景{pic_name}音乐{filename}")
            final_clip.write_videofile(video_mp3_output)
            return final_clip
        else:
            print(f"已存在合并背景{pic_name}音乐{filename}")
            # 读取代码视频
            my_clip = VideoFileClip(video_mp3_output)
            return my_clip

    def merge_text(self, mp3filename, lrc):
        print(f"开始生成字幕mp3{mp3filename}")
        if 'lrc_list' in lrc:
            lrc_texts = []
            for x in lrc['lrc_list'][1:]:
                start = x["begin_time"]
                duration = x["end_time"] - start
                text = x["text"]
                if len(text) == 0:
                    text = " "
                print(start, duration, text)
                txt = (TextClip(text, fontsize=60, font=self.font_style, size=(40 * len(text), None),
                                align='center', color='white', method="caption", stroke_color="black", stroke_width=1.5)
                       .set_position(("center", self.height / 2))
                       .set_duration(duration).set_start(start).crossfadein(1).crossfadeout(1))
                lrc_texts.append(txt)
            print(f"已成生成字幕mp3{mp3filename}")
            return lrc_texts
        else:
            assert "没有歌词信息"


demo = Demo(root_input_path='./input', root_output_path="./output")
